
                              Rise of the Triad

                                 Version 1.1

                                 Hacker Info

RTL & RTC File format:
----------------------

Rise of the Triad (ROTT) uses two file extensions for levels data, RTL
and RTC.  RTC indicates that the file is for Comm-bat (multiplayer) play
only and does not contain any enemies or exits.  RTL indicates the file
can can be used for both Comm-bat and standard game levels.  In Comm-bat,
the enemies in RTL maps in standard play are not present during Comm-bat
games and the exit and entrance arches behave like teleporters.  Other than
these differences, the two files are alike.

The RTL/RTC file format changed with the release of ROTT version 1.1.
Since the shareware version of ROTT cannot use alternate levels, this
should not be a problem for map designers.  The new format is much more
formal.  If any changes are made in the format in the future,  the first 8
bytes of the file will inform you if it is compatible with your editor/viewer.

The RTL/RTC file is broken into three sections:  Version info, Header block,
and Data block.


                            RTL/RTC version info

This 8 byte block of data indicates what type of file it is and which
version of the RTL/RTC file format it is.

  Offset   Size    Description
-------------------------------------------------------------
    0        4     Format signature
    4        4     Version number

Format signature :

This is used to indicate what type of levels are contained within the
file.  This is a null-terminated string containing either "RTL" or "RTC".

Version number :

0101h for version 1.1.  If this value is higher, it indicates that the file
format has changed.  This is NOT the ROTT version.


                            RTL/RTC Header block

The header block contains an array of 100 structures with the following
format:

  Offset   Size    Explanation
-------------------------------------------------------------
    0        4     Used flag
    4        4     CRC
    8        4     RLEWtag
   12        4     MapSpecials
   12        4     Offset in file of Wall plane
   16        4     Offset in file of Sprite plane
   20        4     Offset in file of Info plane
   24        4     Length of Wall plane
   28        4     Length of Sprite plane
   32        4     Length of Info plane
   36       24     Name of level


Used flag :

This is non-zero if a map exists at this position.

CRC :

This value is used to determine if all the players in a multiplayer game
are using the same maps.  You can use any method you like to calculate this
value.

RLEWtag :

This is the run-length encoding tag used for compressing and decompressing
the map data.  The use of this will be described below.

MapSpecials :

This is used for flags that describe special conditions for the level.
Currently only one flag is used.  If Bit 0 is set, then all the pushwalls
will be activated in Comm-bat mode.  This is done in case there are player
start locations within hidden areas and the player would be trapped until
a pushwall was activated.

Offsets :

The Wall, Sprite, and Info plane offsets are each absolute offsets of the
data from the beginning of the file.

Lengths :

The Wall, Sprite, and Info plane lengths are each lengths of the
uncompressed data.

Name of level :

This is a null-terminated string containing the name of the level.
Although there is 24 bytes available, level names should be at most 22
bytes long.

                             RTL/RTC Data block

When expanded, ROTT maps contain 3 planes of 128 by 128 word sized data.
They are stored in the RTL/RTC files as 3 blocks of run-length encoded
data.  The procedure for decompressing them is as follows:

    1) Allocate 128 * 128 words of memory (32768 bytes)
    2) Read one word from compressed block
    3) If word is equal to RLEWTag, then the next two words are a compressed
       run of data.  The first word is the number of words to write.
       The second word is the value to write map.
       If word was not equal to RLEWTag, then simply write that word
       to the map.
    4) Go back to 2 until all data is written.

Here's an example of the procedure in C.


/*---------------------------------------------------------------------
   Function: RLEW_Expand

   Run-length encoded word decompression.
---------------------------------------------------------------------*/

void RLEW_Expand
   (
   unsigned short *source,
   unsigned short *dest,
   long length,
   unsigned short rlewtag
   )

   {
   unsigned short value;
   unsigned short count;
   unsigned short *end;

   end = dest + length;

   while( dest < end );
      {
      value = *source;
      source++;

      if ( value != rlewtag )
         {
         //
         // uncompressed data
         //
         *dest = value;
         dest++;
         }
      else
         {
         //
         // compressed string
         //
         count = *source;
         source++;

         value = *source;
         source++;

         //
         // expand the data
         //
         while( count > 0 )
            {
            *dest = value;
            dest++;
            count--;
            }
         }
      }
   }


Here is sample code for loading a ROTT map.


#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <io.h>

/*---------------------------------------------------------------------
   Map constants
---------------------------------------------------------------------*/

#define MAXLEVELNAMELENGTH           23
#define ALLOCATEDLEVELNAMELENGTH     24
#define NUMPLANES                    3
#define NUMHEADEROFFSETS             100
#define MAPWIDTH                     128
#define MAPHEIGHT                    128
#define MAP_SPECIAL_TOGGLE_PUSHWALLS 0x0001

#define WALL_PLANE    0
#define SPRITE_PLANE  1
#define INFO_PLANE    2

/*---------------------------------------------------------------------
   Type definitions
---------------------------------------------------------------------*/

typedef struct
   {
   unsigned long used;
   unsigned long CRC;
   unsigned long RLEWtag;
   unsigned long MapSpecials;
   unsigned long planestart[ NUMPLANES ];
   unsigned long planelength[ NUMPLANES ];
   char          Name[ ALLOCATEDLEVELNAMELENGTH ];
   } RTLMAP;


/*---------------------------------------------------------------------
   Global variables
---------------------------------------------------------------------*/

unsigned short *mapplanes[ NUMPLANES ];


/*---------------------------------------------------------------------
   Macros
---------------------------------------------------------------------*/

#define MAPSPOT( x, y, plane ) \
   ( mapplanes[ plane ][ MAPWIDTH * ( y ) + ( x ) ] )

#define WALL_AT( x, y )   ( MAPSPOT( ( x ), ( y ), WALL_PLANE ) )
#define SPRITE_AT( x, y ) ( MAPSPOT( ( x ), ( y ), SPRITE_PLANE ) )
#define INFO_AT( x, y )   ( MAPSPOT( ( x ), ( y ), INFO_PLANE ) )


/*---------------------------------------------------------------------
   Function: ReadROTTMap

   Read a map from a RTL/RTC file.
---------------------------------------------------------------------*/

void ReadROTTMap
   (
   char *filename,
   int mapnum
   )

   {
   char            RTLSignature[ 4 ];
   unsigned long   RTLVersion;
   RTLMAP          RTLMap;
   int             filehandle;
   long            pos;
   long            compressed;
   long            expanded;
   int             plane;
   unsigned short *buffer;

   filehandle = open( filename, O_RDONLY | O_BINARY );

   //
   // Load RTL signature
   //
   read( filehandle, RTLSignature, sizeof( RTLSignature ) );

   //
   // Read the version number
   //
   read( filehandle, &RTLVersion, sizeof( RTLVersion ) );

   //
   // Load map header
   //
   lseek( filehandle, mapnum * sizeof( RTLMap ), SEEK_CUR );
   read( filehandle, &RTLMap, sizeof( RTLMap ) );

   if ( !RTLMap.used )
      {
      //
      // Exit on error
      //
      printf( "ReadROTTMap: Tried to load a non existent map!" );
      exit( 1 );
      }

   //
   // load the planes in
   //
   expanded = MAPWIDTH * MAPHEIGHT * 2;

	for( plane = 0; plane <= 2; plane++ )
      {
      pos        = RTLMap.planestart[ plane ];
      compressed = RTLMap.planelength[ plane ];
      buffer     = malloc( compressed );

      lseek( filehandle, pos, SEEK_SET );
      read( filehandle, buffer, compressed );

      mapplanes[ plane ] = malloc( expanded );

      RLEW_Expand( buffer, mapplanes[ plane ], expanded >> 1, RTLMap.RLEWtag );

      free( buffer );
      }

   close( filehandle );
   }


MAP WEIRDNESS
-------------
You can pretty much figure out most of the map data easily, but there are
a few things in the map which are a little oddly set up.  Here's a few
helpful items.


THE UPPER CORNER
The first row of a map contains vital information to setting up a map.

In the first plane (WALLS) are these values:

0,0   FLOOR # (0xB4 through 0xC3, though we might cut some)
1,0   CEILING # (0xC6 through 0xD5, or skies: 0xEA to 0xEE)
2,0   BRIGHTNESS LEVEL (0xD8 to 0xDF, from dark to light)
3,0   RATE AT WHICH LIGHT FADES OUT WITH DISTANCE
        (0xFC to 0x010B, fast to slow)

In the second plane (SPRITES) are these:

0,0   Height of level
	(1-8 ranges from 0x5A to 0x61, 9-16 is from 0x01C2 to 0x01C9)
1,0   Height that sky is at relative to level (with same 1-16 arrangement)
        (not needed for level with a ceiling)
2,0   Icon for NO FOG (0x68) or FOG (0x69)
3,0   Light sourcing icon (0x8B: if present, lights illuminate walls)

Optional items in the upper corner are:

Second Plane
	Lightning icon (0x0179)
	Timer icon (0x79: third plane points 0xXXYY to X,Y location of
	  timed thing--time in minutes/seconds there is MMSS in decimal
	  digits, so 0130 is 1 minute thirty seconds--and to one side of
	  that timed thing is the end time in the same format. This, for
	  instance, would say when to shut the door that opened at the
	  start time)

Third Plane (INFO)
	Song number: 0xBAnn, where nn is song number.  If not present,
	 the game will choose song 0.  If greater than the number of
	 level songs (18 in shareware), the game will blow out.


DISKS
Gravitational Anomaly Disks (GADS) are set up with a GAD icon in the
second plane and a height in the third plane.  The actual graphic has a
disk in the top quarter, so to put one on the floor, you sort of have to
put the object IN the floor, so the disk will be at the right height.
Heights for objects start with 0xB0 and have that last byte as a
tiles-off-the-floor nybble and sixteenths-of-a-tile fraction.

So 0xB000 is, for normal sprites, resting on the floor.

For disks, that would be a disk you could stand on to be one story
(eight feet) in the air. The heights of disks usually go by sixes (that's
the maximum they can be apart and you can still climb them like stairs) or
fours (for a more gradual ascension).  Here are three sets of height
values.  The values of 0xB0F1-$B0FE are into the floor, and $B0F6 is right
about floor height.

by 6     by 4     by 2
B0F6     B0F6     B0F6
B0FC     B0FA     B0F8
B002     B0FE     B0FA
B008     B002     B0FC
B00E     B006     B0FE
B014     B00A     B010
B01A     B00E     B012
B020     B012     B014
B026     B016     ...
B02C     B01A
B032     B01E
B038     B022
B03E     B026
B044     B02A
B04A     B02E
B050     B032
B056     B036
B05C     B03A
B062     B03E
B068     B042
B06E     B046
B074     B04A
B07A     B04E

If you need higher ones, calculate them yourself, man.


SWITCHES AND TOUCHPLATES

Everything activated by a switch or touchplates points to the switch or
touchplate that activates it, with the standard 0xXXYY format.  This way
tons of things can be activated by one switch.  To make a door open with
multiple switches/touchplates, make it a few tiles wide and have different
parts of the door point to the different switches.

LOCKED DOORS
Locked doors are normal doors with a key sprite icon placed on them.


============================================================================

The ROTT WAD Format
-------------------

        Most of you out there are probably very familiar with the WAD file
format developed by Id Software.  We borrowed the format with their
consent and use it for all the data in Rise of the Triad.

        The WAD structure itself is identical to that of other WAD's,
where the WAD header is as follows:

typedef struct
{
	char	identification[4];
	long	numlumps;
	long	infotableofs;
} wadinfo_t;

and the WAD directory is made up of [numlumps] of:

typedef struct
{
	long		filepos;
	long		size;
	char		name[8];
} lumpinfo_t;


ROTT Specific Data
------------------

WALLS - Walls are stored in the WAD between the two labels "WALLSTRT" and
"WALLSTOP".  The format of each wall is a 4,096 block of data with no
header.  The bitmaps are grabbed in vertical posts so that drawing in
modex is more straight format.  All walls must be 64 x 64. The walls must
be the first few lumps in the WAD.

MASKED OBJECTS - Masked objects in the wad comprise all actors and
sprites.  They can be found as weapons, objects, actors etc.  They use the
following headers and structures:

typedef struct
{
   short          origsize;         // the orig size of "grabbed" gfx
   short          width;            // bounding box size
   short          height;
   short          leftoffset;       // pixels to the left of origin
   short          topoffset;        // pixels above the origin
   unsigned short collumnofs[320];  // only [width] used, the [0] is &collumnofs[width]
} patch_t;

These are extremely similar to the patches used in another game, except
for the addition of the origsize parameter.

typedef struct
{
   short origsize;         // the orig size of "grabbed" gfx
   short width;            // bounding box size
   short height;
   short leftoffset;       // pixels to the left of origin
   short topoffset;        // pixels above the origin
   short translevel;
   short collumnofs[320];  // only [width] used, the [0] is &collumnofs[width]
} transpatch_t;

Certain objects in the game like masked walls and touch plates will use
the second type of patch which acts like a translucent patch.

SKYS, FLOORS and CEILINGS - Skys are larger than the screen and are made
up of two 256X200 grabs in posts similar to the walls.  The first grab
represents the bottom part of the sky and the second part the top of the
sky.  The skys are denoted by the labels SKYSTRT and SKYSTOP.  Floors and
ceilings use the following structure:

typedef struct
{
   short     width,height;
   short     orgx,orgy;
   byte     data;
} lpic_t;

They can be found between the labels UPDNSTRT and UPDNSTOP.


Okay, enough hints!  Have fun figuring stuff out.

--THE DEVELOPERS OF INCREDIBLE POWER
